package net.wangds.web.components;

import net.wangds.common.base.funcs.Functions;
import net.wangds.log.helper.LogHelper;
import net.wangds.web.annotations.Auth;
import net.wangds.web.annotations.AuthType;
import net.wangds.web.module.AuthRequest;
import net.wangds.web.module.RequestToMethodItem;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

@Component
public class SpringWebComp {

    public AuthRequest requestAuth(HttpServletRequest req){
        AuthRequest res = new AuthRequest();

        List<RequestToMethodItem> items = findControlMethods(req);
        RequestToMethodItem item = items.stream().findFirst().orElse(null);
        if(item==null
                || StringUtils.equalsAnyIgnoreCase(item.getControllerName()
                ,"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController"
                ,"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController"
                )
                || StringUtils.equalsAnyIgnoreCase(item.getRequestMethodName()
                ,"error","errorHtml")
        ){
            res.setType(AuthType.NO);
            return res;
        }

        String className = item.getControllerName();
        String methodName = item.getRequestMethodName();
        Class[] argTypes = item.getMethodParamTypes();

        try {
            Class clz = Class.forName(className);
            Method method = clz.getDeclaredMethod(methodName, argTypes);

            Auth methodAnn = method.getAnnotation(Auth.class);
            Auth clzAnn = (Auth)clz.getAnnotation(Auth.class);

            Auth ann = methodAnn==null?clzAnn:methodAnn;
            if(ann!=null){
                res.setType(ann.type());

                BiConsumer<String, Throwable> onError = (c,e)->LogHelper.error(()->"权限配置内容"+c+","+e.getLocalizedMessage(),e);

                Functions.each(ann.requireAnyPositions(),c->res.getRequireAnyPositions().add(c),onError);
                Functions.each(ann.requireAnyPrivileges(),c->res.getRequireAnyPrivileges().add(c),onError);
                Functions.each(ann.requireAnyRoles(),c->res.getRequireAnyRoles().add(c),onError);
                Functions.each(ann.requireAnyUsers(),c->res.getRequireAnyUsers().add(c),onError);
                Functions.each(ann.requirePositions(),c->res.getRequirePositions().add(c),onError);
                Functions.each(ann.requirePrivileges(),c->res.getRequirePrivileges().add(c),onError);
                Functions.each(ann.requireRoles(),c->res.getRequireRoles().add(c),onError);
                Functions.each(ann.requireUsers(),c->res.getRequireUsers().add(c),onError);

                return res;
            }


        } catch (ClassNotFoundException | NoSuchMethodException e) {
            LogHelper.error(e);
        }

        return res;
    }

    public List<RequestToMethodItem> findControlMethods(HttpServletRequest request)
    {
        ServletContext servletContext = request.getSession().getServletContext();
        if (servletContext == null)
        {
            return null;
        }
        List<RequestToMethodItem> requestToMethodItemList = new ArrayList<>();
        WebApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);

        //请求url和处理方法的映射
        //List<RequestToMethodItem> requestToMethodItemList = new ArrayList<RequestToMethodItem>();
        //获取所有的RequestMapping
        Map<String, HandlerMapping> allRequestMappings = BeanFactoryUtils.beansOfTypeIncludingAncestors(appContext,
                HandlerMapping.class, true, false);

        for (HandlerMapping handlerMapping : allRequestMappings.values())
        {
            //本项目只需要RequestMappingHandlerMapping中的URL映射
            if (handlerMapping instanceof RequestMappingHandlerMapping)
            {
                RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) handlerMapping;
                Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
                for (Map.Entry<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodEntry : handlerMethods.entrySet())
                {
                    RequestMappingInfo requestMappingInfo = requestMappingInfoHandlerMethodEntry.getKey();
                    HandlerMethod mappingInfoValue = requestMappingInfoHandlerMethodEntry.getValue();

                    RequestMethodsRequestCondition methodCondition = requestMappingInfo.getMethodsCondition();
                    String requestType = methodCondition.getMethods().stream().map(vo -> String.valueOf(vo)).collect(Collectors.joining(","));


                    PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
                    //  String requestUrl = SetUtils.first(patternsCondition.getPatterns());
                    String requestUrl = patternsCondition.getPatterns().stream().map(vo -> String.valueOf(vo)).collect(Collectors.joining(","));;
                    String controllerName = mappingInfoValue.getBeanType().getName();
                    String requestMethodName = mappingInfoValue.getMethod().getName();
                    Class<?>[] methodParamTypes = mappingInfoValue.getMethod().getParameterTypes();
                    RequestToMethodItem item = new RequestToMethodItem(requestUrl, requestType, controllerName, requestMethodName,
                            methodParamTypes);

                    requestToMethodItemList.add(item);
                }
            }
        }
        return requestToMethodItemList;
    }

}
